#pragma once

#include "checker_piece.h"
#include "checker_direction.h"

#include <iostream>
#include <utility>
#include <stack>
#include <set>
#include <deque>
#include <time.h>

#define SIZE_AMERICAN	(8)

class checker_board
{
public:
	checker_board();
	~checker_board()
	{
		clear_board();
	}

	void deploy_piece(PIECE_TYPE type, COORD x, COORD y);
	void undeploy_piece(COORD x, COORD y);
	void relocate_piece(checker_piece* piece, COORD from_x, COORD from_y, COORD to_x, COORD to_y);
	checker_piece* get_piece(COORD x, COORD y);
	void clear_board();
	
	bool jump(checker_piece* piece, COORD to_x, COORD to_y);
	int search_consec_jump_route(PIECE_TYPE type, COORD from_x, COORD from_y, COORD to_x, COORD to_y, size_t depth);

	bool move(checker_piece* piece, COORD to_x, COORD to_y);

	bool check_legal_and_auto_movement(PIECE_TYPE side, bool jump_only, bool* moved);

	PIECE_DIRECTION check_jump_possibility(PIECE_TYPE type, COORD from_x, COORD from_y, COORD to_x, COORD to_y);
	PIECE_DIRECTION check_move_possibility(PIECE_TYPE type, COORD from_x, COORD from_y, COORD to_x, COORD to_y);

	void display();

public:
	inline bool is_opponent(COORD x, COORD y, PIECE_TYPE piece_type)
	{
		return is_empty(x, y)? false : ( board_[x][y]->get_type() == ~piece_type );
	}

	inline bool is_empty(COORD x, COORD y)
	{
		return x < SIZE_AMERICAN && y < SIZE_AMERICAN && (board_[x][y] == NULL);
	}

private:
	static checker_piece* board_[SIZE_AMERICAN][SIZE_AMERICAN];

	std::set<checker_piece*> white_piece_, black_piece_;
	std::stack<PIECE_DIRECTION> route_;
};

checker_piece* checker_board::board_[SIZE_AMERICAN][SIZE_AMERICAN] = {0};

checker_board::checker_board()
{
	//	W,E,W,E,E,E,B,E,
	//	E,W,E,E,E,B,E,B,
	//	W,E,W,E,E,E,B,E,
	//	E,W,E,E,E,B,E,B,
	//	W,E,W,E,E,E,B,E,
	//	E,W,E,E,E,B,E,B,
	//	W,E,W,E,E,E,B,E,
	//	E,W,E,E,E,B,E,B
	deploy_piece(WHITE,0,0); deploy_piece(WHITE,2,0); deploy_piece(WHITE,4,0); deploy_piece(WHITE,6,0);
	deploy_piece(WHITE,1,1); deploy_piece(WHITE,3,1); deploy_piece(WHITE,5,1); deploy_piece(WHITE,7,1);
	deploy_piece(WHITE,0,2); deploy_piece(WHITE,2,2); deploy_piece(WHITE,4,2); deploy_piece(WHITE,6,2);

	deploy_piece(BLACK,1,5); deploy_piece(BLACK,3,5); deploy_piece(BLACK,5,5); deploy_piece(BLACK,7,5);
	deploy_piece(BLACK,0,6); deploy_piece(BLACK,2,6); deploy_piece(BLACK,4,6); deploy_piece(BLACK,6,6);
	deploy_piece(BLACK,1,7); deploy_piece(BLACK,3,7); deploy_piece(BLACK,5,7); deploy_piece(BLACK,7,7);

	
	srand(_time32(NULL));
}

void checker_board::deploy_piece(PIECE_TYPE type, COORD x, COORD y)
{
	checker_piece* piece = new checker_piece(type, x, y);
	board_[x][y] = piece;
	if( type == WHITE )
	{
		white_piece_.insert(piece);
	}
	else
	{
		black_piece_.insert(piece);
	}
}

void checker_board::undeploy_piece(COORD x, COORD y)
{
	checker_piece* piece = board_[x][y];
	if( piece && piece->get_type() == WHITE )
	{
		white_piece_.erase(piece);
	}
	else
	{
		black_piece_.erase(piece);
	}

	delete board_[x][y];
	board_[x][y] = NULL;
}

void checker_board::relocate_piece(checker_piece* piece, COORD from_x, COORD from_y, COORD to_x, COORD to_y)
{
	board_[to_x][to_y] = board_[from_x][from_y];
	board_[from_x][from_y] = NULL;

	piece->set_x(to_x);
	piece->set_y(to_y);
}

checker_piece* checker_board::get_piece(COORD x, COORD y)
{
	return board_[x][y];
}

void checker_board::clear_board()
{
	for(size_t x = 0; x < SIZE_AMERICAN; ++x)
		for(size_t y = 0; y < SIZE_AMERICAN; ++y)
			undeploy_piece(x, y);
}

bool checker_board::check_legal_and_auto_movement(PIECE_TYPE side, bool jump_only, bool* moved)
{
	std::set<checker_piece*>& pieces = (WHITE == side)?white_piece_:black_piece_;

	int jump_distance[4][2] = {{2,2},{2,-2},{-2,2},{-2,-2}};
	int move_distance[4][2] = {{1,1},{1,-1},{-1,1},{-1,-1}};

	std::deque<std::pair<size_t, checker_piece*>> jump_list;
	std::deque<std::pair<size_t, checker_piece*>> move_list;

	std::set<checker_piece*>::iterator itr = pieces.begin();
	for(; itr != pieces.end(); ++itr)
	{
		checker_piece* piece = (*itr);
		COORD x = piece->get_x();
		COORD y = piece->get_y();

		for(size_t i = 0; i < 4; ++i)
		{
			if( STANDSTILL != check_jump_possibility(piece->get_type(), x, y, x+jump_distance[i][0], y+jump_distance[i][1]) )
			{
				jump_list.push_back(std::pair<size_t, checker_piece*>(i, piece));
			}

			if( STANDSTILL != check_move_possibility(piece->get_type(), x, y, x+move_distance[i][0], y+move_distance[i][1]) )
			{
				move_list.push_back(std::pair<size_t, checker_piece*>(i, piece));
			}
		}
	}

	if( jump_list.size() > 0 )
	{
		size_t list_i = (rand() & (jump_list.size()-1));
		size_t distance_i = jump_list[list_i].first;
		checker_piece* piece = jump_list[list_i].second;

		jump(piece, piece->get_x()+jump_distance[distance_i][0], piece->get_y()+jump_distance[distance_i][1]);
		*moved = true;
	
		return true;
	}

	if( move_list.size() > 0 )
	{
		if( !jump_only )
		{
			size_t list_i = (rand() & (move_list.size()-1));
			size_t distance_i = move_list[list_i].first;
			checker_piece* piece = move_list[list_i].second;

			move(piece, piece->get_x()+move_distance[distance_i][0], piece->get_y()+move_distance[distance_i][1]);
			*moved = true;
		}
		return true;
	}

	return false; // no legal_move
}

bool checker_board::jump(checker_piece* piece, COORD to_x, COORD to_y)
{
	COORD x = piece->get_x();
	COORD y = piece->get_y();

	size_t max_depth = search_consec_jump_route(piece->get_type(), x, y, to_x, to_y, 1);
	route_.push(checker_direction::get(x, y, to_x, to_y));

	do
	{
		PIECE_DIRECTION d = route_.top();
		
		if( d == UP_RIGHT )
		{
			undeploy_piece(x+1, y+1);
			x += 2; y += 2;
			std::cout << "[" << ((piece->get_type() == WHITE)?"WHITE":"BLACK") << "] jumped up right" << std::endl;
		}
		else if( d == UP_LEFT )
		{
			undeploy_piece(x-1, y+1);
			x -= 2; y += 2;
			std::cout << "[" << ((piece->get_type() == WHITE)?"WHITE":"BLACK") << "] jumped up left" << std::endl;
		}
		else if( d == DOWN_RIGHT )
		{
			undeploy_piece(x+1, y-1);
			x += 2; y -= 2;
			std::cout << "[" << ((piece->get_type() == WHITE)?"WHITE":"BLACK") << "] jumped down right" << std::endl;
		}
		else if( d == DOWN_LEFT )
		{
			undeploy_piece(x-1, y-1);
			x -= 2; y -= 2;
			std::cout << "[" << ((piece->get_type() == WHITE)?"WHITE":"BLACK") << "] jumped down left" << std::endl;
		}
		else
		{
			std::cout << "[" << ((piece->get_type() == WHITE)?"WHITE":"BLACK") << "] no jump" << std::endl;
		}

		route_.pop();
	}
	while(!route_.empty());

	std::cout << "[" << ((piece->get_type() == WHITE)?"WHITE":"BLACK") << "] " << char('a'+piece->get_x()) << "," << piece->get_y()+1 << " to " << char('a'+x) << "," << y+1 << std::endl;
	relocate_piece(piece, piece->get_x(), piece->get_y(), x, y);

	return max_depth > 0;
}

PIECE_DIRECTION checker_board::check_jump_possibility(PIECE_TYPE type, COORD from_x, COORD from_y, COORD to_x, COORD to_y)
{
	if( false == is_empty(to_x, to_y) )
	{
		//std::cout << "not empty" << std::endl;
		return STANDSTILL;
	}

	if(WHITE == type && to_x == from_x+2 && to_y == from_y+2 && is_opponent(from_x+1, from_y+1, type))
	{
		return UP_RIGHT;
	}
	else if(WHITE == type && to_x == from_x-2 && to_y == from_y+2 && is_opponent(from_x-1, from_y+1, type))
	{
		return UP_LEFT;
	}
	else if(BLACK == type && to_x == from_x+2 && to_y == from_y-2 && is_opponent(from_x+1, from_y-1, type))
	{
		return DOWN_RIGHT;
	}
	else if(BLACK == type && to_x == from_x-2 && to_y == from_y-2 && is_opponent(from_x-1, from_y-1, type))
	{
		return DOWN_LEFT;
	}
	
	return STANDSTILL;
}

PIECE_DIRECTION checker_board::check_move_possibility(PIECE_TYPE type, COORD from_x, COORD from_y, COORD to_x, COORD to_y)
{
	if(false == is_empty(to_x, to_y) )
	{
		return STANDSTILL;
	}

	if(WHITE == type && to_x == from_x+1 && to_y == from_y+1)
	{
		return UP_RIGHT;
	}
	else if(WHITE == type && to_x == from_x-1 && to_y == from_y+1)
	{
		return UP_LEFT;
	}
	else if(BLACK == type && to_x == from_x+1 && to_y == from_y-1)
	{
		return DOWN_RIGHT;
	}
	else if(BLACK == type && to_x == from_x-1 && to_y == from_y-1)
	{
		return DOWN_LEFT;
	}

	return STANDSTILL;
}

int checker_board::search_consec_jump_route(PIECE_TYPE type, COORD from_x, COORD from_y, COORD to_x, COORD to_y, size_t depth)
{
	if( STANDSTILL == check_jump_possibility(type, from_x, from_y, to_x, to_y) )
	{
		return depth-1;
	}

	int next_to_y = to_y+(type==WHITE?2:-2);

	size_t max_depth_left		= search_consec_jump_route(type, to_x, to_y, to_x-2, next_to_y, depth+1);
	size_t max_depth_right		= search_consec_jump_route(type, to_x, to_y, to_x+2, next_to_y, depth+1);
	
	if( max_depth_left > depth || max_depth_right > depth )
	{
		int d = ((max_depth_left>max_depth_right)?LEFT:RIGHT) | ((type==WHITE)?UP:DOWN);
		route_.push((PIECE_DIRECTION)d);
	}

	return std::max(max_depth_left, max_depth_right);
}


bool checker_board::move(checker_piece* piece, COORD to_x, COORD to_y)
{
	if( NULL == piece || false == is_empty(to_x, to_y) )
	{
		std::cout << "not empty" << std::endl;
		return false;
	}

	COORD x = piece->get_x();
	COORD y = piece->get_y();

	if( STANDSTILL == check_move_possibility(piece->get_type(), x, y, to_x, to_y) )
	{
		return false;
	}

	relocate_piece(piece, x, y, to_x, to_y);

	std::cout << "[" << ((piece->get_type() == WHITE)?"WHITE":"BLACK") << "] moved " << char('a'+x) << "," << y+1 << " to " << char('a'+to_x) << "," << to_y+1 << std::endl;

	return true;
}

void checker_board::display()
{
	size_t white_piece_count = 0, black_piece_count = 0;

	std::cout << std::endl << "  a b c d e f g h  " << std::endl;

	char c = 0;

	for(int y = SIZE_AMERICAN-1; y >= 0; --y)
	{
		std::cout << y+1 << " ";

		for(int x = 0; x < SIZE_AMERICAN; ++x)
		{
			checker_piece* piece = board_[x][y];
			
			if(piece)
			{			
				if(piece->get_type() == WHITE)
				{
					++white_piece_count;
					c = 'O';
				}
				else if(piece->get_type() == BLACK)
				{
					++black_piece_count;
					c = 'X';
				}
			}
			else
			{
				c = '.';
			}

			std::cout << c << " ";
		}

		std::cout << y+1 << std::endl;
	}
	std::cout <<  "  a b c d e f g h  " << std::endl;
	std::cout << std::endl << "Black pieces : " << black_piece_count << std::endl << "White pieces : " << white_piece_count << std::endl << std::endl;
}